home *** CD-ROM | disk | FTP | other *** search
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- NNNNAAAAMMMMEEEE
- perlsec - Perl security
-
- DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
- Perl is designed to make it easy to program securely even when running
- with extra privileges, like setuid or setgid programs. Unlike most
- command line shells, which are based on multiple substitution passes on
- each line of the script, Perl uses a more conventional evaluation scheme
- with fewer hidden snags. Additionally, because the language has more
- builtin functionality, it can rely less upon external (and possibly
- untrustworthy) programs to accomplish its purposes.
-
- Perl automatically enables a set of special security checks, called _t_a_i_n_t
- _m_o_d_e, when it detects its program running with differing real and
- effective user or group IDs. The setuid bit in Unix permissions is mode
- 04000, the setgid bit mode 02000; either or both may be set. You can
- also enable taint mode explicitly by using the ----TTTT command line flag. This
- flag is _s_t_r_o_n_g_l_y suggested for server programs and any program run on
- behalf of someone else, such as a CGI script. Once taint mode is on, it's
- on for the remainder of your script.
-
- While in this mode, Perl takes special precautions called _t_a_i_n_t _c_h_e_c_k_s to
- prevent both obvious and subtle traps. Some of these checks are
- reasonably simple, such as verifying that path directories aren't
- writable by others; careful programmers have always used checks like
- these. Other checks, however, are best supported by the language itself,
- and it is these checks especially that contribute to making a set-id Perl
- program more secure than the corresponding C program.
-
- You may not use data derived from outside your program to affect
- something else outside your program--at least, not by accident. All
- command line arguments, environment variables, locale information (see
- the _p_e_r_l_l_o_c_a_l_e manpage), results of certain system calls (readdir,
- readlink, the gecos field of getpw* calls), and all file input are marked
- as "tainted". Tainted data may not be used directly or indirectly in any
- command that invokes a sub-shell, nor in any command that modifies files,
- directories, or processes. Any variable set to a value derived from
- tainted data will itself be tainted, even if it is logically impossible
- for the tainted data to alter the variable. Because taintedness is
- associated with each scalar value, some elements of an array can be
- tainted and others not.
-
- For example:
-
- $arg = shift; # $arg is tainted
- $hid = $arg, 'bar'; # $hid is also tainted
- $line = <>; # Tainted
- $line = <STDIN>; # Also tainted
- open FOO, "/home/me/bar" or die $!;
- $line = <FOO>; # Still tainted
- $path = $ENV{'PATH'}; # Tainted, but see below
- $data = 'abc'; # Not tainted
-
-
-
- PPPPaaaaggggeeee 1111
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- system "echo $arg"; # Insecure
- system "/bin/echo", $arg; # Secure (doesn't use sh)
- system "echo $hid"; # Insecure
- system "echo $data"; # Insecure until PATH set
-
- $path = $ENV{'PATH'}; # $path now tainted
-
- $ENV{'PATH'} = '/bin:/usr/bin';
- delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
-
- $path = $ENV{'PATH'}; # $path now NOT tainted
- system "echo $data"; # Is secure now!
-
- open(FOO, "< $arg"); # OK - read-only file
- open(FOO, "> $arg"); # Not OK - trying to write
-
- open(FOO,"echo $arg|"); # Not OK, but...
- open(FOO,"-|")
- or exec 'echo', $arg; # OK
-
- $shout = `echo $arg`; # Insecure, $shout now tainted
-
- unlink $data, $arg; # Insecure
- umask $arg; # Insecure
-
- exec "echo $arg"; # Insecure
- exec "echo", $arg; # Secure (doesn't use the shell)
- exec "sh", '-c', $arg; # Considered secure, alas!
-
- @files = <*.c>; # Always insecure (uses csh)
- @files = glob('*.c'); # Always insecure (uses csh)
-
- If you try to do something insecure, you will get a fatal error saying
- something like "Insecure dependency" or "Insecure PATH". Note that you
- can still write an insecure ssssyyyysssstttteeeemmmm or eeeexxxxeeeecccc, but only by explicitly doing
- something like the last example above.
-
- LLLLaaaauuuunnnnddddeeeerrrriiiinnnngggg aaaannnndddd DDDDeeeetttteeeeccccttttiiiinnnngggg TTTTaaaaiiiinnnntttteeeedddd DDDDaaaattttaaaa
-
- To test whether a variable contains tainted data, and whose use would
- thus trigger an "Insecure dependency" message, check your nearby CPAN
- mirror for the _T_a_i_n_t._p_m module, which should become available around
- November 1997. Or you may be able to use the following _i_s__t_a_i_n_t_e_d()
- function.
-
- sub is_tainted {
- return ! eval {
- join('',@_), kill 0;
- 1;
- };
- }
-
-
-
-
- PPPPaaaaggggeeee 2222
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- This function makes use of the fact that the presence of tainted data
- anywhere within an expression renders the entire expression tainted. It
- would be inefficient for every operator to test every argument for
- taintedness. Instead, the slightly more efficient and conservative
- approach is used that if any tainted value has been accessed within the
- same expression, the whole expression is considered tainted.
-
- But testing for taintedness gets you only so far. Sometimes you have
- just to clear your data's taintedness. The only way to bypass the
- tainting mechanism is by referencing subpatterns from a regular
- expression match. Perl presumes that if you reference a substring using
- $1, $2, etc., that you knew what you were doing when you wrote the
- pattern. That means using a bit of thought--don't just blindly untaint
- anything, or you defeat the entire mechanism. It's better to verify that
- the variable has only good characters (for certain values of "good")
- rather than checking whether it has any bad characters. That's because
- it's far too easy to miss bad characters that you never thought of.
-
- Here's a test to make sure that the data contains nothing but "word"
- characters (alphabetics, numerics, and underscores), a hyphen, an at
- sign, or a dot.
-
- if ($data =~ /^([-\@\w.]+)$/) {
- $data = $1; # $data now untainted
- } else {
- die "Bad data in $data"; # log this somewhere
- }
-
- This is fairly secure because /\w+/ doesn't normally match shell
- metacharacters, nor are dot, dash, or at going to mean something special
- to the shell. Use of /.+/ would have been insecure in theory because it
- lets everything through, but Perl doesn't check for that. The lesson is
- that when untainting, you must be exceedingly careful with your patterns.
- Laundering data using regular expression is the _O_N_L_Y mechanism for
- untainting dirty data, unless you use the strategy detailed below to fork
- a child of lesser privilege.
-
- The example does not untaint $data if use locale is in effect, because
- the characters matched by \w are determined by the locale. Perl
- considers that locale definitions are untrustworthy because they contain
- data from outside the program. If you are writing a locale-aware
- program, and want to launder data with a regular expression containing
- \w, put no locale ahead of the expression in the same block. See the
- SECURITY entry in the _p_e_r_l_l_o_c_a_l_e manpage for further discussion and
- examples.
-
- SSSSwwwwiiiittttcccchhhheeeessss OOOOnnnn tttthhhheeee """"####!!!!"""" LLLLiiiinnnneeee
-
- When you make a script executable, in order to make it usable as a
- command, the system will pass switches to perl from the script's #!
- line. Perl checks that any command line switches given to a setuid (or
- setgid) script actually match the ones set on the #! line. Some Unix and
-
-
-
- PPPPaaaaggggeeee 3333
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- Unix-like environments impose a one-switch limit on the #! line, so you
- may need to use something like -wU instead of -w -U under such systems.
- (This issue should arise only in Unix or Unix-like environments that
- support #! and setuid or setgid scripts.)
-
- CCCClllleeeeaaaannnniiiinnnngggg UUUUpppp YYYYoooouuuurrrr PPPPaaaatttthhhh
-
- For "Insecure $ENV{PATH}" messages, you need to set $ENV{'PATH'} to a
- known value, and each directory in the path must be non-writable by
- others than its owner and group. You may be surprised to get this
- message even if the pathname to your executable is fully qualified. This
- is _n_o_t generated because you didn't supply a full path to the program;
- instead, it's generated because you never set your PATH environment
- variable, or you didn't set it to something that was safe. Because Perl
- can't guarantee that the executable in question isn't itself going to
- turn around and execute some other program that is dependent on your
- PATH, it makes sure you set the PATH.
-
- It's also possible to get into trouble with other operations that don't
- care whether they use tainted values. Make judicious use of the file
- tests in dealing with any user-supplied filenames. When possible, do
- opens and such aaaafffftttteeeerrrr properly dropping any special user (or group!)
- privileges. Perl doesn't prevent you from opening tainted filenames for
- reading, so be careful what you print out. The tainting mechanism is
- intended to prevent stupid mistakes, not to remove the need for thought.
-
- Perl does not call the shell to expand wild cards when you pass ssssyyyysssstttteeeemmmm
- and eeeexxxxeeeecccc explicit parameter lists instead of strings with possible shell
- wildcards in them. Unfortunately, the ooooppppeeeennnn, gggglllloooobbbb, and backtick functions
- provide no such alternate calling convention, so more subterfuge will be
- required.
-
- Perl provides a reasonably safe way to open a file or pipe from a setuid
- or setgid program: just create a child process with reduced privilege who
- does the dirty work for you. First, fork a child using the special ooooppppeeeennnn
- syntax that connects the parent and child by a pipe. Now the child
- resets its ID set and any other per-process attributes, like environment
- variables, umasks, current working directories, back to the originals or
- known safe values. Then the child process, which no longer has any
- special permissions, does the ooooppppeeeennnn or other system call. Finally, the
- child passes the data it managed to access back to the parent. Because
- the file or pipe was opened in the child while running under less
- privilege than the parent, it's not apt to be tricked into doing
- something it shouldn't.
-
- Here's a way to do backticks reasonably safely. Notice how the eeeexxxxeeeecccc is
- not called with a string that the shell could expand. This is by far the
- best way to call something that might be subjected to shell escapes: just
- never call the shell at all.
-
-
-
-
-
-
- PPPPaaaaggggeeee 4444
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- use English;
- die "Can't fork: $!" unless defined $pid = open(KID, "-|");
- if ($pid) { # parent
- while (<KID>) {
- # do something
- }
- close KID;
- } else {
- my @temp = ($EUID, $EGID);
- $EUID = $UID;
- $EGID = $GID; # XXX: initgroups() not called
- # Make sure privs are really gone
- ($EUID, $EGID) = @temp;
- die "Can't drop privileges" unless
- $UID == $EUID and
- $GID eq $EGID; # String test
- $ENV{PATH} = "/bin:/usr/bin";
- exec 'myprog', 'arg1', 'arg2' or
- die "can't exec myprog: $!";
- }
-
- A similar strategy would work for wildcard expansion via glob, although
- you can use readdir instead.
-
- Taint checking is most useful when although you trust yourself not to
- have written a program to give away the farm, you don't necessarily trust
- those who end up using it not to try to trick it into doing something
- bad. This is the kind of security checking that's useful for set-id
- programs and programs launched on someone else's behalf, like CGI
- programs.
-
- This is quite different, however, from not even trusting the writer of
- the code not to try to do something evil. That's the kind of trust
- needed when someone hands you a program you've never seen before and
- says, "Here, run this." For that kind of safety, check out the Safe
- module, included standard in the Perl distribution. This module allows
- the programmer to set up special compartments in which all system
- operations are trapped and namespace access is carefully controlled.
-
- SSSSeeeeccccuuuurrrriiiittttyyyy BBBBuuuuggggssss
-
- Beyond the obvious problems that stem from giving special privileges to
- systems as flexible as scripts, on many versions of Unix, set-id scripts
- are inherently insecure right from the start. The problem is a race
- condition in the kernel. Between the time the kernel opens the file to
- see which interpreter to run and when the (now-set-id) interpreter turns
- around and reopens the file to interpret it, the file in question may
- have changed, especially if you have symbolic links on your system.
-
- Fortunately, sometimes this kernel "feature" can be disabled.
- Unfortunately, there are two ways to disable it. The system can simply
- outlaw scripts with any set-id bit set, which doesn't help much.
-
-
-
- PPPPaaaaggggeeee 5555
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- Alternately, it can simply ignore the set-id bits on scripts. If the
- latter is true, Perl can emulate the setuid and setgid mechanism when it
- notices the otherwise useless setuid/gid bits on Perl scripts. It does
- this via a special executable called ssssuuuuiiiiddddppppeeeerrrrllll that is automatically
- invoked for you if it's needed.
-
- However, if the kernel set-id script feature isn't disabled, Perl will
- complain loudly that your set-id script is insecure. You'll need to
- either disable the kernel set-id script feature, or put a C wrapper
- around the script. A C wrapper is just a compiled program that does
- nothing except call your Perl program. Compiled programs are not
- subject to the kernel bug that plagues set-id scripts. Here's a simple
- wrapper, written in C:
-
- #define REAL_PATH "/path/to/script"
- main(ac, av)
- char **av;
- {
- execv(REAL_PATH, av);
- }
-
- Compile this wrapper into a binary executable and then make _i_t rather
- than your script setuid or setgid.
-
- See the program wwwwrrrraaaappppssssuuuuiiiidddd in the _e_g directory of your Perl distribution
- for a convenient way to do this automatically for all your setuid Perl
- programs. It moves setuid scripts into files with the same name plus a
- leading dot, and then compiles a wrapper like the one above for each of
- them.
-
- In recent years, vendors have begun to supply systems free of this
- inherent security bug. On such systems, when the kernel passes the name
- of the set-id script to open to the interpreter, rather than using a
- pathname subject to meddling, it instead passes /_d_e_v/_f_d/_3. This is a
- special file already opened on the script, so that there can be no race
- condition for evil scripts to exploit. On these systems, Perl should be
- compiled with -DSETUID_SCRIPTS_ARE_SECURE_NOW. The CCCCoooonnnnffffiiiigggguuuurrrreeee program
- that builds Perl tries to figure this out for itself, so you should never
- have to specify this yourself. Most modern releases of SysVr4 and BSD
- 4.4 use this approach to avoid the kernel race condition.
-
- Prior to release 5.003 of Perl, a bug in the code of ssssuuuuiiiiddddppppeeeerrrrllll could
- introduce a security hole in systems compiled with strict POSIX
- compliance.
-
- PPPPrrrrooootttteeeeccccttttiiiinnnngggg YYYYoooouuuurrrr PPPPrrrrooooggggrrrraaaammmmssss
-
- There are a number of ways to hide the source to your Perl programs, with
- varying levels of "security".
-
-
-
-
-
-
- PPPPaaaaggggeeee 6666
-
-
-
-
-
-
- PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111)))) PPPPEEEERRRRLLLLSSSSEEEECCCC((((1111))))
-
-
-
- First of all, however, you _c_a_n'_t take away read permission, because the
- source code has to be readable in order to be compiled and interpreted.
- (That doesn't mean that a CGI script's source is readable by people on
- the web, though.) So you have to leave the permissions at the socially
- friendly 0755 level.
-
- Some people regard this as a security problem. If your program does
- insecure things, and relies on people not knowing how to exploit those
- insecurities, it is not secure. It is often possible for someone to
- determine the insecure things and exploit them without viewing the
- source. Security through obscurity, the name for hiding your bugs
- instead of fixing them, is little security indeed.
-
- You can try using encryption via source filters (Filter::* from CPAN).
- But crackers might be able to decrypt it. You can try using the byte
- code compiler and interpreter described below, but crackers might be able
- to de-compile it. You can try using the native-code compiler described
- below, but crackers might be able to disassemble it. These pose varying
- degrees of difficulty to people wanting to get at your code, but none can
- definitively conceal it (this is true of every language, not just Perl).
-
- If you're concerned about people profiting from your code, then the
- bottom line is that nothing but a restrictive licence will give you legal
- security. License your software and pepper it with threatening
- statements like "This is unpublished proprietary software of XYZ Corp.
- Your access to it does not give you permission to use it blah blah blah."
- You should see a lawyer to be sure your licence's wording will stand up
- in court.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PPPPaaaaggggeeee 7777
-
-
-
-